#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <strings.h>    //提供字符串操作函数,比 <string.h> 更基础或特定的函数
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"

using namespace LogModule;

using func_t = std::function<std::string(const std::string&)>;

const int default_fd = -1;

class UdpServer
{
public:
    UdpServer(uint16_t port)
        :_socket_fd(default_fd)
        ,_port(port)
        ,_isrunning(false)
    {}

    void Init()
    {
        //创建套接字->打开文件
        //AF_INET：使用 IPv4 地址，SOCK_DGRAM：数据报通信
        //0：协议类型， 0表示自动选择协议，SOCK_DGRAM 类型->UDP
        _socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_socket_fd < 0)
        {
            LOG(LogLevel::FATAL) << "create socket error!";
            exit(1);
        }
        LOG(LogLevel::INFO) << "create socket success, socket_fd: " << _socket_fd;

        //绑定socket信息，设置ip、端口
        struct sockaddr_in local;
        //对local初始化（清0）
        bzero(&local, sizeof(local));

        local.sin_family = AF_INET;
        //htons(): host to network short，将 主机字节序 转换为 网络字节序。
        //主机字节序：大端或小端
        //网络字节序：网络协议标准规定的字节序（大端）
        local.sin_port = htons(_port);
        //inet_addr(): 将 IPv4 地址的点分十进制表示（如 "192.168.1.1"）转换为 32 位的网络字节序二进制格式（即 in_addr_t 类型）的函数
        //local.sin_addr.s_addr = inet_addr(_ip.c_str());
        //绑定固定的ip， 客户端必须向固定ip发送信息
        //现实情况可能存在一台服务端主机对应多个ip，绑定固定ip只能接受客户端固定ip的信息
        local.sin_addr.s_addr = INADDR_ANY;
        
        //将套接字绑定到特定的本地地址和端口上。
        int res = bind(_socket_fd, (struct sockaddr*)&local, sizeof(local));
        if(res < 0)
        {
            LOG(LogLevel::FATAL) << "bind error!";
            exit(2);
        }
        LOG(LogLevel::INFO) << "bind success, socket_fd: " << _socket_fd;
    }

    void Start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t res = recvfrom(_socket_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if(res > 0)
            {
                //远端（客户端）的端口号
                uint16_t peer_port = ntohs(peer.sin_port);
                std::string peer_ip = inet_ntoa(peer.sin_addr);
                buffer[res] = 0;
                LOG(LogLevel::DEBUG) << "[" << peer_ip << ":" << peer_port << "]# " << buffer;

                //发送给客户端的消息
                std::string message = buffer;
                sendto(_socket_fd, message.c_str(), message.size(), 0, (struct sockaddr*)&peer, len);
            }
        }
    }

    ~UdpServer()
    {}
private:
    int _socket_fd;
    uint16_t _port;
    //std::string _ip;    //点分十进制
    bool _isrunning;
};